﻿using OfficeOpenXml;
using OfficeOpenXml.Style;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using System.Xml.XPath;

namespace Tambourine.Model
{
    /// <summary>
    /// Presents collection of test result pivot tables
    /// </summary>
    public class TestSuite : ObservableCollection<TestCortegeMatrix>
    {

        public TestSuite(string filePath)
        {
            Load(filePath);
        }

        #region Load xml report

        /// <summary>
        /// Load data from NUnit xml file
        /// </summary>
        /// <param name="filePath">xml file path</param>
        public void Load(string filePath)
        {
            if (!File.Exists(filePath))
                throw new ArgumentException("File does not exist");

            XDocument doc = XDocument.Load(filePath);
            this.Clear();

            doc.Descendants("test-suite").Where(s => s.Elements("properties").Count() == 1)
                                         .ToList().ForEach(a => TryToAddSuite(a));
        }

        private void TryToAddSuite(XElement xmlTestSuite)
        {
            string tambColumn = xmlTestSuite.Element("properties").Elements()
                                .Where(a => a.Attribute("name").Value == "TambourineColumn")
                                .FirstOrDefault().Attribute("value").Value;

            string tambArgList = xmlTestSuite.Element("properties").Elements()
                                .Where(a => a.Attribute("name").Value == "TambourineArguments")
                                .FirstOrDefault().Attribute("value").Value;

            string suiteName = xmlTestSuite.Attribute("name").Value;
            bool suiteWasExecuted = bool.Parse(xmlTestSuite.Attribute("executed").Value);
            bool suiteIsSuccessful = bool.Parse(xmlTestSuite.Attribute("success").Value);

            TestCortegeMatrix suite = new TestCortegeMatrix(suiteName, suiteIsSuccessful, suiteWasExecuted, tambArgList, tambColumn);

            foreach (XElement xmlTest in xmlTestSuite.Element("results").Elements())
            {
                string testName = xmlTest.Attribute("name").Value;

                TestResult test = new TestResult();
                test.WasExecuted = bool.Parse(xmlTest.Attribute("executed").Value);
                test.IsSuccessful = bool.Parse(xmlTest.Attribute("success").Value);
                test.FailureMesssage = SelectElementValue(xmlTest, "failure/message");
                test.FailureStackTrace = SelectElementValue(xmlTest, "failure/stack-trace");

                suite.AddTestResult(testName, test);
            }

            this.Add(suite);
        }

        private string SelectElementValue(XContainer xdoc, string xmlPath)
        {
            XElement data = xdoc.XPathSelectElement(xmlPath);
            return (data != null) ? data.Value : null;
        }

        #endregion Load xml report

        #region OpenXml Report output

        /// <summary>
        /// Export data to xlsx file
        /// </summary>
        /// <param name="fileName">output file name</param>
        /// <param name="matrixName">test name to export (optional)</param>
        public void ExcelExport(string fileName, string matrixName = null)
        {
            if (!this.Any()  || (matrixName != null && !this.Any(a => a.Name == matrixName)))
                throw new ArgumentException("there are no data to export");

            using (ExcelPackage p = new ExcelPackage())
            {
                if (matrixName == null)
                    foreach (TestCortegeMatrix matrix in this)
                        ExportMatrixToReport(p, matrix);
                else
                    if(this.Any(a => a.Name == matrixName))
                        ExportMatrixToReport(p, this.First(a => a.Name == matrixName));

                using (FileStream fs = new FileStream(fileName, FileMode.Create))
                {
                    p.SaveAs(fs);
                    fs.Close();
                }
            }
        }

        private void ExportMatrixToReport(ExcelPackage p, TestCortegeMatrix matrix)
        {
            const int startRowNum = 2;
            const int startColumnNum = 1;

            p.Workbook.Worksheets.Add(matrix.Name);
            ExcelWorksheet ws = p.Workbook.Worksheets[matrix.Name];

            int rowNum = startRowNum;
            int columnNum = startColumnNum;
            Dictionary<string, int> columnNumbers = new Dictionary<string, int>();

            //headers
            foreach (PropertyDescriptor prop in matrix.GetItemProperties(null))
            {
                ExcelRange header = ws.Cells[rowNum, columnNum];
                header.Value = prop.Name;

                //common header style
                ExcelFill fill = header.Style.Fill;
                fill.PatternType = ExcelFillStyle.Solid;
                fill.BackgroundColor.SetColor(Color.LightGreen);

                //result header style
                if (prop.PropertyType == typeof(TestResult))
                {
                    header.Style.TextRotation = 90;
                    header.Style.VerticalAlignment = ExcelVerticalAlignment.Bottom;
                    header.Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
                }
                else //method header style
                {
                    header.Style.VerticalAlignment = ExcelVerticalAlignment.Center;
                    header.Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
                }

                columnNumbers.Add(prop.Name, columnNum);
                columnNum++;
            }

            ws.Cells[rowNum, startColumnNum, rowNum, --columnNum].AutoFilter = true;

            rowNum++;

            //values
            foreach (TestCortege cortege in matrix)
            {
                foreach (string columnName in columnNumbers.Keys)
                {
                    ExcelRange valueCell = ws.Cells[rowNum, columnNumbers[columnName]];
                    object valueObject = cortege[columnName];

                    //result style
                    TestResult result = valueObject as TestResult;
                    if (result != null)
                    {
                        valueCell.Value = result.IsSuccessful.ToString();

                        valueCell.Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
                        if (result.IsSuccessful)
                            valueCell.Style.Font.Color.SetColor(Color.Green);
                        else
                        {
                            valueCell.Style.Font.Color.SetColor(Color.Red);
                            valueCell.AddComment(result.FailureMesssage + "\n" + result.FailureStackTrace, "Tambourine");
                        }
                    }

                    else //method style
                        valueCell.Value = valueObject.ToString();
                }

                rowNum++;
            }

            //column width
            for (int column = columnNum; column > 0; column--)
                ws.Column(column).AutoFit();

            //common format
            ExcelStyle commonStyle = ws.Cells[startRowNum, startColumnNum, --rowNum, columnNum].Style;
            commonStyle.Border.Left.Style = ExcelBorderStyle.Thin;
            commonStyle.Border.Top.Style = ExcelBorderStyle.Thin;
            commonStyle.Border.Right.Style = ExcelBorderStyle.Thin;
            commonStyle.Border.Bottom.Style = ExcelBorderStyle.Thin;
        }

        #endregion
    }
}
